// g_turret.c

#include "g_local.h"
#include "botsqdev.h"
#include "botsmain.h"


void AnglesNormalize(vec3_t vec)
{
	while(vec[0] > 360)
		vec[0] -= 360;
	while(vec[0] < 0)
		vec[0] += 360;
	while(vec[1] > 360)
		vec[1] -= 360;
	while(vec[1] < 0)
		vec[1] += 360;
}

float SnapToEights(float x)
{
	x *= 8.0;
	if (x > 0.0)
		x += 0.5;
	else
		x -= 0.5;
	return 0.125 * (int)x;
}


void turret_blocked(edict_t *self, edict_t *other)
{
	edict_t	*attacker;

	// BEGIN Teamplay
	if (self->owner == other)
		return;
	// END Teamplay
	
	if (other->takedamage)
	{
		if (self->teammaster->owner)
			attacker = self->teammaster->owner;
		else
			attacker = self->teammaster;
		T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
	}
}

/*QUAKED turret_breach (0 0 0) ?
This portion of the turret can change both pitch and yaw.
The model  should be made with a flat pitch.
It (and the associated base) need to be oriented towards 0.
Use "angle" to set the starting angle.

"speed"		default 50
"dmg"		default 10
"angle"		point this forward
"target"	point this at an info_notnull at the muzzle tip
"minpitch"	min acceptable pitch angle : default -30
"maxpitch"	max acceptable pitch angle : default 30
"minyaw"	min acceptable yaw angle   : default 0
"maxyaw"	max acceptable yaw angle   : default 360
*/

void turret_breach_fire (edict_t *self)
{
	vec3_t	f, r, u;
	vec3_t	start;
	int		damage;
	int		speed;

	AngleVectors (self->s.angles, f, r, u);
	VectorMA (self->s.origin, self->move_origin[0], f, start);
	VectorMA (start, self->move_origin[1], r, start);
	VectorMA (start, self->move_origin[2], u, start);

	// BEGIN teamplay
	if (!deathmatch->value)
		damage = 100 + random() * 50;
	else
		damage = 300 + random() * 50;
	// END teamplay

	speed = 550 + 50 * skill->value;
	fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage);
	gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
}

void turret_breach_think (edict_t *self)
{
	edict_t	*ent;
	vec3_t	current_angles;
	vec3_t	delta;
	// BEGIN teamplay
	qboolean	leaveturret = false; 
	gitem_t		*it;
	vec3_t		target, forward;
	int			i;
	// END teamplay

	VectorCopy (self->s.angles, current_angles);
	AnglesNormalize(current_angles);

	AnglesNormalize(self->move_angles);
	if (self->move_angles[PITCH] > 180)
		self->move_angles[PITCH] -= 360;

	// clamp angles to mins & maxs
	if (self->move_angles[PITCH] > self->pos1[PITCH])
		self->move_angles[PITCH] = self->pos1[PITCH];
	else if (self->move_angles[PITCH] < self->pos2[PITCH])
		self->move_angles[PITCH] = self->pos2[PITCH];

	if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
	{
		float	dmin, dmax;

		dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
		if (dmin < -180)
			dmin += 360;
		else if (dmin > 180)
			dmin -= 360;
		dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
		if (dmax < -180)
			dmax += 360;
		else if (dmax > 180)
			dmax -= 360;
		if (fabs(dmin) < fabs(dmax))
			self->move_angles[YAW] = self->pos1[YAW];
		else
			self->move_angles[YAW] = self->pos2[YAW];
	}

	VectorSubtract (self->move_angles, current_angles, delta);
	if (delta[0] < -180)
		delta[0] += 360;
	else if (delta[0] > 180)
		delta[0] -= 360;
	if (delta[1] < -180)
		delta[1] += 360;
	else if (delta[1] > 180)
		delta[1] -= 360;
	delta[2] = 0;

	if (delta[0] > self->speed * FRAMETIME)
		delta[0] = self->speed * FRAMETIME;
	if (delta[0] < -1 * self->speed * FRAMETIME)
		delta[0] = -1 * self->speed * FRAMETIME;
	if (delta[1] > self->speed * FRAMETIME)
		delta[1] = self->speed * FRAMETIME;
	if (delta[1] < -1 * self->speed * FRAMETIME)
		delta[1] = -1 * self->speed * FRAMETIME;

	VectorScale (delta, 1.0/FRAMETIME, self->avelocity);

	self->nextthink = level.time + FRAMETIME;

	for (ent = self->teammaster; ent; ent = ent->teamchain)
		ent->avelocity[1] = self->avelocity[1];

	// BEGIN Teamplay	
	// if we have a driver, adjust his velocities
	if (self->owner)
	{
//		if (!(deathmatch->value) && (self->owner->target_ent == self))
		if (!(deathmatch->value))
		{
			float	angle;
			float	target_z;
			float	diff;
			vec3_t	target;
			vec3_t	dir;

			// angular is easy, just copy ours
			self->owner->avelocity[0] = self->avelocity[0];
			self->owner->avelocity[1] = self->avelocity[1];

			// x & y
			angle = self->s.angles[1] + self->owner->move_origin[1];
			angle *= (M_PI*2 / 360);
			target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
			target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
			target[2] = self->owner->s.origin[2];

			VectorSubtract (target, self->owner->s.origin, dir);
			self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
			self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;

			// z
			angle = self->s.angles[PITCH] * (M_PI*2 / 360);
			target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);

			diff = target_z - self->owner->s.origin[2];
			self->owner->velocity[2] = diff * 1.0 / FRAMETIME;

			if (self->spawnflags & 65536)
			{
				turret_breach_fire (self);
				self->spawnflags &= ~65536;
			}
		}
		else if ((self->owner->health > 0) && (self->owner->hasturret == true))
		{		
			// a player is controlling the turret, move towards view angles
			for (i=0; i<3; i++)
				self->move_angles[i] = self->owner->client->v_angle[i];

			AnglesNormalize(self->move_angles);

			// FIXME: do a tracebox from up and behind towards the turret, to try and keep them from
			// getting stuck inside the rotating turret
			// x & y
			AngleVectors(self->s.angles, forward, NULL, NULL);
			VectorScale(forward, 32, forward);
			VectorSubtract(self->s.origin, forward, target);

			VectorAdd(target, tv(0,0,8), self->owner->s.origin);

			gi.linkentity(self->owner);

			// should the turret shoot now?
			if ((self->owner->client->buttons & BUTTON_ATTACK) && (self->delay < level.time))	// 3 second break between shots
			{
				if (self->owner->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))])
				{
					turret_breach_fire (self);
					self->delay = level.time + 1.0;

					self->owner->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]--;
				}
				else
				{
					gsutil_centerprint(self->owner, "%s", "You're out of rockets for the turret!");
					leaveturret = true;
				}
			}
			else if (!(self->owner->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]))
			{
				gsutil_centerprint(self->owner, "%s", "You're out of rockets for the turret!");
				leaveturret = true;
			}
		} 
		else
			leaveturret = true;

		// has the player abondoned the turret?
		if ((leaveturret == true) || (self->owner->exitturret))
		{
			// level the gun
			self->move_angles[0] = 0;

			ent = self->owner;
			it = FindItem ("Blaster");
			if (!it)
				return;
			ent->client->newweapon = it;
			ChangeWeapon(ent);

			// throw them back from turret
//			AngleVectors(self->s.angles, forward, NULL, NULL);
//			VectorScale(forward, -300, forward);
//			forward[2] = forward[2] + 150;
//			if (forward[2] < 80)
//				forward[2] = 80;

//			for (i=0; i<3; i++)
//				ent->velocity[i] = forward[i];
			
//			ent->s.origin[2] = ent->s.origin[2] + 1;
			ent->movetype = MOVETYPE_WALK;
//			ent->gravity = 1;

			// turn ON client side prediction for this player
			gi.WriteByte(11);				// 11 = svc_stufftext
			gi.WriteString("cl_predict 1\n");
			gi.unicast(ent, 1);

			gi.linkentity (ent);

//			self->owner->hasturret = false;
			self->owner->exitturret = false;
			self->owner = NULL;

			gsmod_QueueCommand(self, "zzzturretzzz");
		}
	} 
	else 
	{	
		// check if a player has mounted the turret

		// find a player
		edict_t	*ent;
		vec3_t	target, forward;
		vec3_t	dir;
		int		i;

		ent = &g_edicts[0];
		ent++;
		for (i=0; i<maxclients->value; i++, ent++)
		{
			if (!ent->inuse)
				continue;

			// determine distance from turret seat location

			// x & y
			AngleVectors(self->s.angles, forward, NULL, NULL);
			VectorScale(forward, 32, forward);
			VectorSubtract(self->s.origin, forward, target);

			VectorSubtract (target, ent->s.origin, dir);
			if (fabs(dir[2]) < 64)
				dir[2] = 0;

			if ((VectorLength(dir) < 16) && 
				((ent->client->pers.player_class == 1) || 
					(ent->client->pers.player_class == 2) || 
					(ent->client->pers.player_class == 4) || 
					(ent->client->pers.player_class == 5)) && 
				(ent->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]) )
			{	
				// player has taken control of turret
				self->owner = ent;
				ent->hasturret = true;
//				ent->movetype = MOVETYPE_PUSH;	// don't let them move, or they'll get stuck
				ent->movetype = MOVETYPE_NONE;	// don't let them move, or they'll get stuck
//				ent->gravity = 0;

				// turn off client side prediction for this player
				gi.WriteByte(11);				// 11 = svc_stufftext
				gi.WriteString("cl_predict 0\n");
				gi.unicast(ent, 1);

				gi.linkentity(ent);
				stuffcmd(ent, "+use\n");
			}

		}
	}
	// END Teamplay	
}

void turret_breach_finish_init (edict_t *self)
{
	// get and save info for muzzle location
	if (!self->target)
	{
		gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
	}
	else
	{
		self->target_ent = G_PickTarget (self->target);
		VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin);
		G_FreeEdict(self->target_ent);
	}

	self->teammaster->dmg = self->dmg;
	self->think = turret_breach_think;
	self->think (self);
}

void SP_turret_breach (edict_t *self)
{
	self->solid = SOLID_BSP;
	self->movetype = MOVETYPE_PUSH;
	gi.setmodel (self, self->model);

	if (!self->speed)
		self->speed = 50;
	if (!self->dmg)
		self->dmg = 10;

	if (!st.minpitch)
		st.minpitch = -30;
	if (!st.maxpitch)
		st.maxpitch = 30;
	if (!st.maxyaw)
		st.maxyaw = 360;

	self->pos1[PITCH] = -1 * st.minpitch;
	self->pos1[YAW]   = st.minyaw;
	self->pos2[PITCH] = -1 * st.maxpitch;
	self->pos2[YAW]   = st.maxyaw;

	self->ideal_yaw = self->s.angles[YAW];
	self->move_angles[YAW] = self->ideal_yaw;

	self->blocked = turret_blocked;

	self->think = turret_breach_finish_init;
	self->nextthink = level.time + FRAMETIME;
	gi.linkentity (self);
}


/*QUAKED turret_base (0 0 0) ?
This portion of the turret changes yaw only.
MUST be teamed with a turret_breach.
*/

void SP_turret_base (edict_t *self)
{
	self->solid = SOLID_BSP;
	self->movetype = MOVETYPE_PUSH;
	gi.setmodel (self, self->model);
	self->blocked = turret_blocked;
	gi.linkentity (self);
}


/*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
Must NOT be on the team with the rest of the turret parts.
Instead it must target the turret_breach.
*/

void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage);
void infantry_stand (edict_t *self);
void monster_use (edict_t *self, edict_t *other, edict_t *activator);

void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
	edict_t	*ent;

	// level the gun
	self->target_ent->move_angles[0] = 0;

	// remove the driver from the end of them team chain
	for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
		;
	ent->teamchain = NULL;
	self->teammaster = NULL;
	self->flags &= ~FL_TEAMSLAVE;

	self->target_ent->owner = NULL;
	self->target_ent->teammaster->owner = NULL;

	infantry_die (self, inflictor, attacker, damage);
}

qboolean FindTarget (edict_t *self);

void turret_driver_think (edict_t *self)
{
	vec3_t	target;
	vec3_t	dir;
	float	reaction_time;

	self->nextthink = level.time + FRAMETIME;

	if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
		self->enemy = NULL;

	if (!self->enemy)
	{
		if (!FindTarget (self))
			return;
		self->monsterinfo.trail_time = level.time;
		self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
	}
	else
	{
		if (visible (self, self->enemy))
		{
			if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
			{
				self->monsterinfo.trail_time = level.time;
				self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
			}
		}
		else
		{
			self->monsterinfo.aiflags |= AI_LOST_SIGHT;
			return;
		}
	}

	// let the turret know where we want it to aim
	VectorCopy (self->enemy->s.origin, target);
	target[2] += self->enemy->viewheight;
	VectorSubtract (target, self->target_ent->s.origin, dir);
	vectoangles (dir, self->target_ent->move_angles);

	// decide if we should shoot
	if (level.time < self->monsterinfo.attack_finished)
		return;

	reaction_time = (3 - skill->value) * 1.0;
	if ((level.time - self->monsterinfo.trail_time) < reaction_time)
		return;

	self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
	//FIXME how do we really want to pass this along?
	self->target_ent->spawnflags |= 65536;
}

void turret_driver_link (edict_t *self)
{
	vec3_t	vec;
	edict_t	*ent;

	self->think = turret_driver_think;
	self->nextthink = level.time + FRAMETIME;

	self->target_ent = G_PickTarget (self->target);
	self->target_ent->owner = self;
	self->target_ent->teammaster->owner = self;
	VectorCopy (self->target_ent->s.angles, self->s.angles);

	vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
	vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
	vec[2] = 0;
	self->move_origin[0] = VectorLength(vec);

	VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
	vectoangles (vec, vec);
	AnglesNormalize(vec);
	self->move_origin[1] = vec[1];

	self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];

	// add the driver to the end of them team chain
	for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
		;
	ent->teamchain = self;
	self->teammaster = self->target_ent->teammaster;
	self->flags |= FL_TEAMSLAVE;
}

void SP_turret_driver (edict_t *self)
{
	if (deathmatch->value)
	{
		G_FreeEdict (self);
		return;
	}

	self->movetype = MOVETYPE_PUSH;
	self->solid = SOLID_BBOX;
	self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
	VectorSet (self->mins, -16, -16, -24);
	VectorSet (self->maxs, 16, 16, 32);

	self->health = 100;
	self->gib_health = 0;
	self->mass = 200;
	self->viewheight = 24;

	self->die = turret_driver_die;
	self->monsterinfo.stand = infantry_stand;

	self->flags |= FL_NO_KNOCKBACK;

	level.total_monsters++;

	self->svflags |= SVF_MONSTER;
	self->s.renderfx |= RF_FRAMELERP;
	self->takedamage = DAMAGE_AIM;
	self->use = monster_use;
	self->clipmask = MASK_MONSTERSOLID;
	VectorCopy (self->s.origin, self->s.old_origin);
	self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED;

	if (st.item)
	{
		self->item = FindItemByClassname (st.item);
		if (!self->item)
			gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
	}

	self->think = turret_driver_link;
	self->nextthink = level.time + FRAMETIME;

	gi.linkentity (self);
}
